package model.bullet;
import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;

import model.cell.tower.AbstractTower;
import model.enemy.AbstractEnemy;



public class LightningBullet extends AbstractBullet {
	
	private LinkedList<ArrayList<Point>> _lightnings;

	private int _persistence = 3;
	
	private int _displayDuration = 4;
	
	
	public LightningBullet(Point pos, AbstractEnemy enemy, AbstractTower tower) {
		super(pos, enemy, tower);
		_damage = 1;
		color = new Color(170,170,255);
		_refreshSpeed = 10;
	}
	
	
	private AlphaComposite makeComposite(float alpha) {
		int type = AlphaComposite.SRC_OVER;
		return(AlphaComposite.getInstance(type, alpha));
	}
	
	
	public void draw(Graphics g) {
		
		if ( _lightnings.size() < 1 ) return;
		
	    Graphics2D g2d = (Graphics2D) g;
    	Composite originalComposite = g2d.getComposite();
    	
    	float alpha = 0.10f;
    	g2d.setComposite(makeComposite(alpha));
    	g2d.setPaint(Color.WHITE);
    	g2d.setStroke(new BasicStroke( 3.0f ));
    	
    	// TODO optimize
    	for (Iterator<ArrayList<Point>> lightningIter = _lightnings.iterator(); lightningIter.hasNext();) {
	    	
	    	Point lastPoint = new Point((int)_posX, (int)_posY);
	    	ArrayList<Point> lightning = (ArrayList<Point>) lightningIter.next();
			
			for (Iterator<Point> it = lightning.iterator(); it.hasNext();) {
				
				Point randomPoint = (Point) it.next();
		    	
		    	g2d.drawLine(lastPoint.x, lastPoint.y, randomPoint.x, randomPoint.y);
				lastPoint = randomPoint;
			}
		}
    	g2d.setStroke(new BasicStroke( 1.0f ));
    	
	    for (Iterator<ArrayList<Point>> lightningIter = _lightnings.iterator(); lightningIter.hasNext();) {
			
	    	alpha = (float) ((Math.random() * 0.7) + 0.3);
	    	g2d.setComposite(makeComposite(alpha));
	    	g2d.setPaint(color);
	    	
	    	Point lastPoint = new Point((int)_posX, (int)_posY);
	    	ArrayList<Point> lightning = (ArrayList<Point>) lightningIter.next();
			
			for (Iterator<Point> it = lightning.iterator(); it.hasNext();) {
				
				Point randomPoint = (Point) it.next();
				g2d.drawLine(lastPoint.x, lastPoint.y, randomPoint.x, randomPoint.y);
				lastPoint = randomPoint;
			}
		}
	    
	  	g2d.setComposite(originalComposite);
	}
	
	
	public synchronized void move() {
		
		if (_enemy == null || _enemy.getHealth() == 0) { 
			_destroy();
			return;
		}
		
		if ( _displayDuration < _persistence ) {
			
			_displayDuration++;
			_hitEnemy();
			return;
		}
		
		if (!_tower.enemyInRange(_enemy)) {
			_destroy();
			return;
		}
		
		_displayDuration = 0;
		_lightnings = new LinkedList<ArrayList<Point>>();
		
		for (int i = 0; i < 3; i++)
			_lightnings.add(_generateLightning(i));
	}
	
	
	public void setDamage(int damage) {
		
		_damage = damage;
	}
	
	
	private ArrayList<Point> _generateLightning(int lightningNb) {
		
		ArrayList<Point> pointMap = new ArrayList<Point>();
		
		Point towerPos = new Point((int)_posX, (int)_posY);
		Point enemyPos = _enemy.getPosition();
		
		float distanceX = enemyPos.x - towerPos.x;
		float distanceY = enemyPos.y - towerPos.y;
		double distance = Math.sqrt(distanceX*distanceX + distanceY*distanceY);
		
		double distanceLeft = distance;
		int nbPoint = 2 + (int) (Math.random() * distance / 15);
		int startIndex = lightningNb % 2;
		
		for (int i = startIndex; i < nbPoint; i++) {
			
			//double randomDistance = (Math.random() * distanceLeft / 5);
			double randomDistance = (distance / (nbPoint+1));
			pointMap.add(_getDeviantPoint(distanceX, distanceY, distance, distance - distanceLeft + randomDistance, (i % 2 == 0)));
			distanceLeft -= randomDistance;
		}
		
		pointMap.add((Point) enemyPos.clone());
		
		return pointMap;
	}
	
	
	private Point _getDeviantPoint(float distanceX, float distanceY, double distance, double randomDistance, boolean signPlus) {
		
		int deviance = 8;
		
		Point trajectoryPoint = new Point((int)(_posX + (randomDistance * distanceX / distance)), (int)(_posY + (randomDistance * distanceY / distance)));
		
		double deviantX = (- distanceY / distance) * ((Math.random() * deviance * 2) - deviance);
		double deviantY = (distanceX / distance) * ((Math.random() * deviance * 2) - deviance);
		return new Point(trajectoryPoint.x + (int)deviantX, trajectoryPoint.y + (int)deviantY);
	}
	
}

